iT邦幫忙

2023 iThome 鐵人賽

DAY 18
0
Security

從自建漏洞中學習 - 一起填坑吧系列 第 18

Auth 應用程式 - Signup API 實踐篇

  • 分享至 

  • xImage
  •  

Auth 應用程式 - Signup API 實踐篇

今天,我們會來介紹在實作註冊功能的新手注意事項~
會簡單解釋 Bad Practice & Good Practice 的差異性,Let's get started!

Signup 篇

  • 需求

    我們會需要使用者輸入以下元素:

    username、email、password、passwordConfirmed

以下暫時省略 Error Handling 的部分:

第一步 - 建立新的 user

Bad Practice

export.signup = async(req,res,next) => {
    // 以下是不好的範例
    const newUser = await User.create(req.body);
    
    res.status(201).json({
        status: 'success',
        data: {
            user: newUser
        }
    });
}

說明:

上述的方法常常會在新手教學中看到,而有時候使用這種直接 create req.body 的方式 ( 或是直接 post、update 整包 req.body ) 很有可能造成無意間幫 user 升級 role 到 admin 了! 因為也會跟著儲存其他欄位的 data 到 DB 內。

Good Practice

export signup = async(req, res, next) => {
    const newUser = await User.create({
        name: req.body.name,
        email: req.body.email,
        password: req.body.password,
        passwordConfirmed: req.body.passwordConfirmed
    });
    
    res.status(201).json({
        status: 'success',
        data: {
            user: newUser
        }
    });

}

說明:

明確指定要 create 那些 req.body.param (這邊的 param 請替換成 column 名稱),避免也跟著新增不必要的資訊。

第二步 - 加入 JWT

在此處我們使用 jsonwebtoken , 在使用之前我們需要先下載他,在 command 中下:

npm i jsonwebtoken

Bad Practice

exports.signup = async (req, res, next) => {
    // 以下是第一步的 good practice
    const newUser = await User.create({
        name: req.body.name,
        email: req.body.email,
        password: req.body.password,
        passwordConfirmed: req.body.passwordConfirmed
    });

    // 主要加入的地方: 以下為需要解說的不良示範
    const token = jwt.sign({ id: newUser._id }, 'secret', { expiresIn: '90d'});

    // 以下是第一步的 good practice
    res.status(201).json({
        status: 'success',
        token,
        data: {
            user: newUser
        }
    });
}

解說:

此處不好的示範有兩點,仔細看一下以下節錄自上面的程式碼:

const token = jwt.sign({ id: newUser._id }, 'secret', { expiresIn: '90d'});
  1. 在此處我們使用 'secret' 作為 jwt 加密的 secret,然而這真的太短了,也是網路上範例常見的,最好是使用你自己專屬的 32 個字符作為 secret
  2. 無論是 secret 或是 token exprireIn ,這邊直接寫在此處,沒有使用一個檔案來管理他 (ex. env),這麼做也會存在另外一個風險,就是當你直接 push 上 github,他就直接被洩漏了XD

Good Practice

exports.signup = async (req, res, next) => {
    // 以下是第一步的 good practice
    const newUser = await User.create({
        name: req.body.name,
        email: req.body.email,
        password: req.body.password,
        passwordConfirmed: req.body.passwordConfirmed
    });

    // 主要加入的地方: 以下為需要解說的比較好的示範
    const token = jwt.sign({ id: newUser._id }, process.env.JWT_SECRET, { expiresIn: process.new.JWT_EXPIREIN});

    // 以下是第一步的 good practice
    res.status(201).json({
        status: 'success',
        token,
        data: {
            user: newUser
        }
    });
}

解說:

比較好的方式就是把 secret 換成專屬於自己持有的 32 個字符,並且把需要管理的參數存在 .env ,最後,記得將 .env
放你的 .gitignore 檔案中。


今日小心得

終於開始進行 Node.js 的安全實踐啦! 真。建立漏洞 (?
雖然最近身體還是有點不適,但我想還是會越來越好的吧 ~


Reference:


上一篇
開始使用 Node.js 建立 Auth 應用程式 - 規劃篇之 2
下一篇
Auth 應用程式 - Login API 實踐篇
系列文
從自建漏洞中學習 - 一起填坑吧30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言